package com.yungee.aspect;

import com.yungee.lock.LockType;
import com.yungee.lock.MemoryLock;
import com.yungee.lock.RedisLock;
import com.yungee.lock.annotation.Lock;
import com.yungee.util.SpelParser;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Component
@Aspect
@Order(0)
@Slf4j
public class LockAspect {
    private static final String POINT = ".";

    public LockAspect() {
    }

    @Pointcut("@annotation(com.yungee.lock.annotation.Lock)")
    public void check() {
    }

    @Before("check()")
    public void before(JoinPoint point) {
        Method targetMethod = ((MethodSignature)point.getSignature()).getMethod();
        Lock anno = (Lock)AnnotatedElementUtils.getMergedAnnotation(targetMethod, Lock.class);
        String key = this.generateLockKey(point, anno);
        log.info("@Lock尝试加锁:{}", key);
        RedisLock lock = new RedisLock(key,anno.expire(), anno.errorno(), anno.msg(), anno.reject(), anno.type());
        boolean isLocked = false;

        try {
            isLocked = lock.tryLock();
        } catch (Exception var8) {
            log.info("@Lock尝试加内存锁:{}", key);
            isLocked = MemoryLock.tryLock(key);
            lock.setMem(true);
        }

        if (isLocked) {
            log.info("@Lock加锁成功:{}", key);
            RedisLock.LOCK_THREAD_LOCAL.set(lock);
        } else {
            if (lock.isMem()) {
                MemoryLock.unlock(key);
            }

            lock.getHandler().handle(lock);
        }

    }

    @AfterReturning("check()")
    public void afterReturning() {
        try {
            RedisLock lock = (RedisLock)RedisLock.LOCK_THREAD_LOCAL.get();
            if (lock != null) {
                if (lock.isMem()) {
                    log.info("@Lock释放内存锁:{}", lock.getName());
                    MemoryLock.unlock(lock.getName());
                } else if (LockType.AUTO == lock.getType()) {
                    log.info("@Lock释放锁:{}", lock.getName());
                    lock.unlock();
                }
            }
        } finally {
            RedisLock.LOCK_THREAD_LOCAL.remove();
        }

    }

    @AfterThrowing("check()")
    public void afterThrowing() {
        if (RedisLock.LOCK_THREAD_LOCAL.get() != null) {
            try {
                RedisLock lock = (RedisLock)RedisLock.LOCK_THREAD_LOCAL.get();
                if (lock.isMem()) {
                    log.info("@Lock释放内存锁:{}", lock.getName());
                    MemoryLock.unlock(lock.getName());
                } else {
                    log.info("@Lock释放锁:{}", lock.getName());
                    lock.unlock();
                }
            } finally {
                RedisLock.LOCK_THREAD_LOCAL.remove();
            }
        }

    }

    private String generateClassMethodName(JoinPoint point) {
        String className = point.getSignature().getDeclaringType().getSimpleName();
        String methodName = point.getSignature().getName();
        return "Lock." + className + "." + methodName;
    }

    private String generateLockKey(JoinPoint point, Lock annotation) {
        String prefix = this.generateClassMethodName(point);
        prefix = prefix + (annotation.value().isEmpty() ? "" : "." + SpelParser.parse(annotation.value(), point));
        return prefix;
    }
}
